package org.imaginedays.androidall.ui;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.imaginedays.androidall.R;
import org.imaginedays.androidall.DownloadProvider.download.DownloadService;
import org.imaginedays.androidall.DownloadProvider.provider.DownloadManager;
import org.imaginedays.androidall.DownloadProvider.provider.DownloadManager.Request;
import org.imaginedays.androidall.DownloadProvider.ui.DownloadCustomListAdapter;
import org.imaginedays.androidall.DownloadProvider.ui.DownloadItem.DownloadSelectListener;
import org.imaginedays.androidall.DownloadProvider.ui.DownloadList;
import org.imaginedays.androidall.constants.Constants;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.Intent;
import android.database.ContentObserver;
import android.database.Cursor;
import android.database.DataSetObserver;
import android.net.Uri;
import android.os.Environment;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;

public class DownloadFilesListACT extends Activity implements OnClickListener,
		DownloadSelectListener, OnCancelListener, OnItemClickListener {

	private static final String TAG = "DownloadFilesListACT";
	private BroadcastReceiver mReceiver;
	private ListView mDateOrderedListView;
	private EditText mUrlInputEditText;
	private DownloadCustomListAdapter mAdapter;

	private DownloadManager mDownloadManager;
	private Cursor mDateSortedCursor;
	private ViewGroup mSelectionMenuView;
	private Button mSelectionDeleteButton;
	private View mEmptyView;

	private int mStatusColumnId;
	private int mIdColumnId;
	private int mLocalUriColumnId;
	private int mMediaTypeColumnId;
	private int mReasonColumndId;

	private Set<Long> mSelectedIds = new HashSet<Long>();
	private Long mQueuedDownloadId = null;
	private AlertDialog mQueuedDialog;

	private MyContentObserver mContentObserver = new MyContentObserver();
	private MyDataSetObserver mDataSetObserver = new MyDataSetObserver();

	private class MyContentObserver extends ContentObserver {
		public MyContentObserver() {
			super(new Handler());
		}

		@Override
		public void onChange(boolean selfChange) {
			Log.e(TAG, "onChange");
			handleDownloadsChanged();
		}
	}

	private class MyDataSetObserver extends DataSetObserver {
		@Override
		public void onChanged() {
			// may need to switch to or from the empty view
			showListData();
		}
	}

	/**
	 * Called when there's a change to the downloads database.
	 */
	void handleDownloadsChanged() {
		checkSelectionForDeletedEntries();

		if (mQueuedDownloadId != null && moveToDownload(mQueuedDownloadId)) {
			Log.e(TAG, "handleDownloadsChanged");
			if (mDateSortedCursor.getInt(mStatusColumnId) != DownloadManager.STATUS_PAUSED
					|| !isPausedForWifi(mDateSortedCursor)) {
				mQueuedDialog.cancel();
			}
		}
	}

	protected void onCreate(android.os.Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		// 设置布局
		setContentView(R.layout.download_files_list);

		// 设置edittext view
		setupViews();

		//
		setupDownloadManager();

		// 设置启动下载服务
		startDownloadService();

		// 查询数据库更新UI
		setDataUI();

		// mReceiver = new BroadcastReceiver() {
		// @Override
		// public void onReceive(Context context, Intent intent) {
		// showDownloadList();
		// }
		// };
		//
		// registerReceiver(mReceiver, new IntentFilter(
		// DownloadManager.ACTION_NOTIFICATION_CLICKED));

		showListData();

	}

	private boolean isPausedForWifi(Cursor cursor) {
		return cursor.getInt(mReasonColumndId) == DownloadManager.PAUSED_QUEUED_FOR_WIFI;
	}

	private long[] getSelectionAsArray() {
		long[] selectedIds = new long[mSelectedIds.size()];
		Iterator<Long> iterator = mSelectedIds.iterator();
		for (int i = 0; i < selectedIds.length; i++) {
			selectedIds[i] = iterator.next();
		}
		return selectedIds;
	}

	/**
	 * Check if any of the selected downloads have been deleted from the
	 * downloads database, and remove such downloads from the selection.
	 */
	private void checkSelectionForDeletedEntries() {
		Log.e(TAG, "checkSelectionForDeletedEntries");
		// gather all existing IDs...
		Set<Long> allIds = new HashSet<Long>();
		for (mDateSortedCursor.moveToFirst(); !mDateSortedCursor.isAfterLast(); mDateSortedCursor
				.moveToNext()) {
			allIds.add(mDateSortedCursor.getLong(mIdColumnId));
		}

		// ...and check if any selected IDs are now missing
		for (Iterator<Long> iterator = mSelectedIds.iterator(); iterator
				.hasNext();) {
			if (!allIds.contains(iterator.next())) {
				iterator.remove();
			}
		}
	}

	/**
	 * Move {@link #mDateSortedCursor} to the download with the given ID.
	 * 
	 * @return true if the specified download ID was found; false otherwise
	 */
	private boolean moveToDownload(long downloadId) {
		for (mDateSortedCursor.moveToFirst(); !mDateSortedCursor.isAfterLast(); mDateSortedCursor
				.moveToNext()) {
			if (mDateSortedCursor.getLong(mIdColumnId) == downloadId) {
				return true;
			}
		}
		return false;
	}

	private void showListData() {
		Log.e(TAG, "showListData");
		if (mDateSortedCursor == null || mDateSortedCursor.getCount() == 0) {
			Log.e(TAG, "listView invisible");
			mEmptyView.setVisibility(View.VISIBLE);
		} else {
			Log.e(TAG, "listView visible");
			mEmptyView.setVisibility(View.GONE);
			activeListView().setVisibility(View.VISIBLE);
			activeListView().invalidateViews(); // ensure checkboxes get updated
			Log.e(TAG, mDateSortedCursor.getCount() + "");
		}

	}

	@Override
	protected void onResume() {
		super.onResume();
		if (haveCursors()) {
			mDateSortedCursor.registerContentObserver(mContentObserver);
			mDateSortedCursor.registerDataSetObserver(mDataSetObserver);
			refresh();
		}
	}

	/**
	 * Requery the database and update the UI.
	 */
	private void refresh() {
		mDateSortedCursor.requery();
		// Adapters get notification of changes and update automatically
		mAdapter.notifyDataSetChanged();
	}

	@Override
	protected void onPause() {
		super.onPause();
		if (haveCursors()) {
			mDateSortedCursor.unregisterContentObserver(mContentObserver);
			mDateSortedCursor.unregisterDataSetObserver(mDataSetObserver);
		}
	}

	/**
	 * @return the ListView that should currently be visible.
	 */
	private ListView activeListView() {
		return mDateOrderedListView;
	}

	private void setupDownloadManager() {
		mDownloadManager = new DownloadManager(getContentResolver(),
				getPackageName());
		mDownloadManager.setAccessAllDownloads(true);

	}

	public void setDataUI() {
		// 构建数据库查询
		DownloadManager.Query baseQuery = new DownloadManager.Query()
				.setOnlyIncludeVisibleInDownloadsUi(true);

		;

		mDateSortedCursor = mDownloadManager.query(baseQuery);

		if (haveCursors()) {
			startManagingCursor(mDateSortedCursor);
			mStatusColumnId = mDateSortedCursor
					.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS);
			mIdColumnId = mDateSortedCursor
					.getColumnIndexOrThrow(DownloadManager.COLUMN_ID);
			mLocalUriColumnId = mDateSortedCursor
					.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_URI);
			mMediaTypeColumnId = mDateSortedCursor
					.getColumnIndexOrThrow(DownloadManager.COLUMN_MEDIA_TYPE);
			mReasonColumndId = mDateSortedCursor
					.getColumnIndexOrThrow(DownloadManager.COLUMN_REASON);

			mDateOrderedListView = (ListView) findViewById(R.id.lv_download_flie_list);
			mAdapter = new DownloadCustomListAdapter(this, mDateSortedCursor,
					this);
			mDateOrderedListView.setAdapter(mAdapter);
			mDateOrderedListView.setOnItemClickListener(this);

		}

	}

	public boolean haveCursors() {
		return mDateSortedCursor != null;

	}

	/**
	 * @return the appropriate error message for the failed download pointed to
	 *         by cursor
	 */
	private String getErrorMessage(Cursor cursor) {
		switch (cursor.getInt(mReasonColumndId)) {
		case DownloadManager.ERROR_FILE_ALREADY_EXISTS:
			if (isOnExternalStorage(cursor)) {
				return getString(R.string.dialog_file_already_exists);
			} else {
				// the download manager should always find a free filename for
				// cache downloads,
				// so this indicates a strange internal error
				return getUnknownErrorMessage();
			}

		case DownloadManager.ERROR_INSUFFICIENT_SPACE:
			if (isOnExternalStorage(cursor)) {
				return getString(R.string.dialog_insufficient_space_on_external);
			} else {
				return getString(R.string.dialog_insufficient_space_on_cache);
			}

		case DownloadManager.ERROR_DEVICE_NOT_FOUND:
			return getString(R.string.dialog_media_not_found);

		case DownloadManager.ERROR_CANNOT_RESUME:
			return getString(R.string.dialog_cannot_resume);

		default:
			return getUnknownErrorMessage();
		}
	}

	private boolean isOnExternalStorage(Cursor cursor) {
		String localUriString = cursor.getString(mLocalUriColumnId);
		if (localUriString == null) {
			return false;
		}
		Uri localUri = Uri.parse(localUriString);
		if (!localUri.getScheme().equals("file")) {
			return false;
		}
		String path = localUri.getPath();
		String externalRoot = Environment.getExternalStorageDirectory()
				.getPath();
		return path.startsWith(externalRoot);
	}

	private String getUnknownErrorMessage() {
		return getString(R.string.dialog_failed_body);
	}

	/**
	 * 设置EditText mEmptyView
	 */
	private void setupViews() {

		mUrlInputEditText = (EditText) findViewById(R.id.et_download_file_path);
		mUrlInputEditText.setText(Constants.DOWNLOAD_PATH);

		findViewById(R.id.btn_start_download).setOnClickListener(this);
		findViewById(R.id.btn_open_download_list).setOnClickListener(this);
		mEmptyView = findViewById(R.id.empty);
		findViewById(R.id.btn_pause_download).setOnClickListener(this);

		mSelectionMenuView = (ViewGroup) findViewById(R.id.selection_menu);
		mSelectionDeleteButton = (Button) findViewById(R.id.selection_delete);
		mSelectionDeleteButton.setOnClickListener(this);
		((Button) findViewById(R.id.deselect_all)).setOnClickListener(this);
		// throw new NullPointerException("测试全局异常捕获");

	}

	private void startDownloadService() {
		Intent intent = new Intent();
		intent.setClass(this, DownloadService.class);
		startService(intent);
	}

	@Override
	protected void onDestroy() {
		// unregisterReceiver(mReceiver);
		super.onDestroy();
	}

	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.btn_start_download:
			startDownload();
			break;

		case R.id.btn_open_download_list:
			// showDownloadList();
			break;
		case R.id.deselect_all:
			clearSelection();
			break;
		}
	}

	private void clearSelection() {
		mSelectedIds.clear();
	}

	long downloadId;

	private void startDownload() {
		String url = mUrlInputEditText.getText().toString();
		Uri srcUri = Uri.parse(url);
		DownloadManager.Request request = new Request(srcUri);
		request.setDestinationInExternalPublicDir(
				Environment.DIRECTORY_DOWNLOADS, "/");
		// request.setDescription("Download Test");
		downloadId = mDownloadManager.enqueue(request);
		Log.e(TAG, "downloadId: " + downloadId);
	}

	// handle a click on one of the download item checkboxes
	@Override
	public void onDownloadSelectionChanged(long downloadId, boolean isSelected) {
		if (isSelected) {
			mSelectedIds.add(downloadId);
		} else {
			mSelectedIds.remove(downloadId);
		}
//		showOrHideSelectionMenu();
	}

	private void showOrHideSelectionMenu() {
		boolean shouldBeVisible = !mSelectedIds.isEmpty();
		boolean isVisible = mSelectionMenuView.getVisibility() == View.VISIBLE;
		if (shouldBeVisible) {
			updateSelectionMenu();
			if (!isVisible) {
				// show menu
				mSelectionMenuView.setVisibility(View.VISIBLE);
				mSelectionMenuView.startAnimation(AnimationUtils.loadAnimation(
						this, R.anim.footer_appear));
			}
		} else if (!shouldBeVisible && isVisible) {
			// hide menu
			mSelectionMenuView.setVisibility(View.GONE);
			mSelectionMenuView.startAnimation(AnimationUtils.loadAnimation(
					this, R.anim.footer_disappear));
		}
	}

	/**
	 * Set up the contents of the selection menu based on the current selection.
	 */
	private void updateSelectionMenu() {
		int deleteButtonStringId = R.string.delete_download;
		if (mSelectedIds.size() == 1) {
			Cursor cursor = mDownloadManager.query(new DownloadManager.Query()
					.setFilterById(mSelectedIds.iterator().next()));
			try {
				cursor.moveToFirst();
				switch (cursor.getInt(mStatusColumnId)) {
				case DownloadManager.STATUS_FAILED:
					deleteButtonStringId = R.string.delete_download;
					break;

				case DownloadManager.STATUS_PENDING:
					deleteButtonStringId = R.string.remove_download;
					break;

				case DownloadManager.STATUS_PAUSED:
				case DownloadManager.STATUS_RUNNING:
					deleteButtonStringId = R.string.cancel_running_download;
					break;
				}
			} finally {
				cursor.close();
			}
		}
		mSelectionDeleteButton.setText(deleteButtonStringId);
	}

	@Override
	public boolean isDownloadSelected(long id) {
		return mSelectedIds.contains(id);
	}

	@Override
	public void onCancel(DialogInterface dialog) {
		mQueuedDownloadId = null;
		mQueuedDialog = null;
	}

	@Override
	public void onItemClick(AdapterView<?> parent, View view, int position,
			long id) {
		mDateSortedCursor.moveToPosition(position);
		handleItemClick(mDateSortedCursor);

	}

	private void handleItemClick(Cursor cursor) {
		long id = cursor.getInt(mIdColumnId);
		switch (cursor.getInt(mStatusColumnId)) {
		case DownloadManager.STATUS_PENDING:
		case DownloadManager.STATUS_RUNNING:
			showRunningDialog(id);
			break;

		case DownloadManager.STATUS_PAUSED:
			if (isPausedForWifi(cursor)) {
				mQueuedDownloadId = id;
				mQueuedDialog = new AlertDialog.Builder(this)
						.setTitle(R.string.dialog_title_queued_body)
						.setMessage(R.string.dialog_queued_body)
						.setPositiveButton(R.string.keep_queued_download, null)
						.setNegativeButton(R.string.remove_download,
								getDeleteClickHandler(id))
						.setOnCancelListener(this).show();
			} else {
				showPausedDialog(id);
			}
			break;

		case DownloadManager.STATUS_SUCCESSFUL:
			// openCurrentDownload(cursor);
			break;

		case DownloadManager.STATUS_FAILED:
			showFailedDialog(id, getErrorMessage(cursor));
			break;
		}
	}

	private void showRunningDialog(long downloadId) {
		new AlertDialog.Builder(this)
				.setTitle(R.string.download_running)
				.setMessage(R.string.dialog_running_body)
				.setNegativeButton(R.string.cancel_running_download,
						getDeleteClickHandler(downloadId))
				.setPositiveButton(R.string.pause_download,
						getPauseClickHandler(downloadId)).show();
	}

	private void showPausedDialog(long downloadId) {
		new AlertDialog.Builder(this)
				.setTitle(R.string.download_queued)
				.setMessage(R.string.dialog_paused_body)
				.setNegativeButton(R.string.delete_download,
						getDeleteClickHandler(downloadId))
				.setPositiveButton(R.string.resume_download,
						getResumeClickHandler(downloadId)).show();
	}

	private void showFailedDialog(long downloadId, String dialogBody) {
		new AlertDialog.Builder(this)
				.setTitle(R.string.dialog_title_not_available)
				.setMessage(dialogBody)
				.setNegativeButton(R.string.delete_download,
						getDeleteClickHandler(downloadId))
				.setPositiveButton(R.string.retry_download,
						getRestartClickHandler(downloadId)).show();
	}

	/**
	 * @return an OnClickListener to delete the given downloadId from the
	 *         Download Manager
	 */
	private DialogInterface.OnClickListener getDeleteClickHandler(
			final long downloadId) {
		return new DialogInterface.OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				deleteDownload(downloadId);
			}
		};
	}

	/**
	 * @return an OnClickListener to pause the given downloadId from the
	 *         Download Manager
	 */
	private DialogInterface.OnClickListener getPauseClickHandler(
			final long downloadId) {
		return new DialogInterface.OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				Log.e("DownloadList", "downloadId: " + downloadId);
				mDownloadManager.pauseDownload(downloadId);
			}
		};
	}

	/**
	 * @return an OnClickListener to resume the given downloadId from the
	 *         Download Manager
	 */
	private DialogInterface.OnClickListener getResumeClickHandler(
			final long downloadId) {
		return new DialogInterface.OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				mDownloadManager.resumeDownload(downloadId);
			}
		};
	}

	/**
	 * @return an OnClickListener to restart the given downloadId in the
	 *         Download Manager
	 */
	private DialogInterface.OnClickListener getRestartClickHandler(
			final long downloadId) {
		return new DialogInterface.OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				mDownloadManager.restartDownload(downloadId);
			}
		};
	}

	/**
	 * Delete a download from the Download Manager.
	 */
	private void deleteDownload(long downloadId) {
		if (moveToDownload(downloadId)) {
			int status = mDateSortedCursor.getInt(mStatusColumnId);
			boolean isComplete = status == DownloadManager.STATUS_SUCCESSFUL
					|| status == DownloadManager.STATUS_FAILED;
			String localUri = mDateSortedCursor.getString(mLocalUriColumnId);
			if (isComplete && localUri != null) {
				String path = Uri.parse(localUri).getPath();
				if (path.startsWith(Environment.getExternalStorageDirectory()
						.getPath())) {
					mDownloadManager.markRowDeleted(downloadId);
					return;
				}
			}
		}
		mDownloadManager.remove(downloadId);
	}
}
